package com.xu.cloud.common.api;

import org.springframework.aop.support.AopUtils;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 * api version RequestMappingHandlerMapping
 *
 * @author xuguan
 * @since 2025/3/10
 */
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override
    protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            Map<Method, RequestMappingInfo> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<RequestMappingInfo>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
                if (logger.isDebugEnabled()) {
                    logger.debug("register handler method: " + invocableMethod + ", mapping: " + mapping);
                }
            });
        }
    }

    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        final ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        if (Objects.isNull(apiVersion)) {
            return null;
        }
        return new ApiVersionRequestCondition(apiVersion.version());
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        final ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        if (Objects.isNull(apiVersion)) {
            return null;
        }
        return new ApiVersionRequestCondition(apiVersion.version());
    }

}
